<!-- directive:breadcrumb ES6学习笔记 -->
<!-- directive:title ES6学习笔记 -->
<div class='panel-body'>
    <p>类的修饰</p>
    <p>许多面向对象的语言都有修饰器（Decorator）函数，用来修改类的行为。目前，有一个提案将这项功能，引入了 ECMAScript。</p>
    <p>@testable</p>
    <p>class MyTestableClass {</p>
    <p>    // ...</p>
    <p>}</p>
    <p>function testable(target) {</p>
    <p>    target.isTestable = true;</p>
    <p>}</p>
    <p>MyTestableClass.isTestable // true</p>
    <p>上面代码中，@testable就是一个修饰器。它修改了MyTestableClass这个类的行为，为它加上了静态属性isTestable。testable函数的参数target是MyTestableClass类本身。</p>
    <p>基本上，修饰器的行为就是下面这样。</p>
    <p>@decorator</p>
    <p>class A {}</p>
    <p>// 等同于</p>
    <p>class A {}</p>
    <p>A = decorator(A) || A;</p>
    <p>也就是说，修饰器是一个对类进行处理的函数。修饰器函数的第一个参数，就是所要修饰的目标类。</p>
    <p>function testable(target) {</p>
    <p>    // ...</p>
    <p>}</p>
    <p>上面代码中，testable函数的参数target，就是会被修饰的类。</p>
    <p>如果觉得一个参数不够用，可以在修饰器外面再封装一层函数。</p>
    <p>function testable(isTestable) {</p>
    <p>    return function(target) {</p>
    <p>        target.isTestable = isTestable;</p>
    <p>    }</p>
    <p>}</p>
    <p>@testable(true)</p>
    <p>class MyTestableClass {}</p>
    <p>MyTestableClass.isTestable // true</p>
    <p>@testable(false)</p>
    <p>class MyClass {}</p>
    <p>MyClass.isTestable // false</p>
    <p>上面代码中，修饰器testable可以接受参数，这就等于可以修改修饰器的行为。</p>
    <p>注意，修饰器对类的行为的改变，是代码编译时发生的，而不是在运行时。这意味着，修饰器能在编译阶段运行代码。也就是说，修饰器本质就是编译时执行的函数。</p>
    <p>前面的例子是为类添加一个静态属性，如果想添加实例属性，可以通过目标类的prototype对象操作。</p>
    <p>function testable(target) {</p>
    <p>    target.prototype.isTestable = true;</p>
    <p>}</p>
    <p>@testable</p>
    <p>class MyTestableClass {}</p>
    <p>let obj = new MyTestableClass();</p>
    <p>obj.isTestable // true</p>
    <p>上面代码中，修饰器函数testable是在目标类的prototype对象上添加属性，因此就可以在实例上调用。</p>
    <p>下面是另外一个例子。</p>
    <p>// mixins.js</p>
    <p>export function mixins(...list) {</p>
    <p>    return function (target) {</p>
    <p>        Object.assign(target.prototype, ...list)</p>
    <p>    }</p>
    <p>}</p>
    <p>// main.js</p>
    <p>import { mixins } from './mixins'</p>
    <p>const Foo = {</p>
    <p>    foo() { console.log('foo') }</p>
    <p>};</p>
    <p>@mixins(Foo)</p>
    <p>class MyClass {}</p>
    <p>let obj = new MyClass();</p>
    <p>obj.foo() // 'foo'</p>
    <p>上面代码通过修饰器mixins，把Foo对象的方法添加到了MyClass的实例上面。可以用Object.assign()模拟这个功能。</p>
    <p>const Foo = {</p>
    <p>    foo() { console.log('foo') }</p>
    <p>};</p>
    <p>class MyClass {}</p>
    <p>Object.assign(MyClass.prototype, Foo);</p>
    <p>let obj = new MyClass();</p>
    <p>obj.foo() // 'foo'</p>
    <p>实际开发中，React 与 Redux 库结合使用时，常常需要写成下面这样。</p>
    <p>class MyReactComponent extends React.Component {}</p>
    <p>export default connect(mapStateToProps, mapDispatchToProps)(MyReactComponent);</p>
    <p>有了装饰器，就可以改写上面的代码。</p>
    <p>@connect(mapStateToProps, mapDispatchToProps)</p>
    <p>export default class MyReactComponent extends React.Component {}</p>
    <p>相对来说，后一种写法看上去更容易理解。</p>
    <p>方法的修饰</p>
    <p>修饰器不仅可以修饰类，还可以修饰类的属性。</p>
    <p>class Person {</p>
    <p>    @readonly</p>
    <p>    name() { return `${this.first} ${this.last}` }</p>
    <p>}</p>
    <p>上面代码中，修饰器readonly用来修饰“类”的name方法。</p>
    <p>修饰器函数readonly一共可以接受三个参数。</p>
    <p>function readonly(target, name, descriptor){</p>
    <p>    // descriptor对象原来的值如下</p>
    <p>    // {</p>
    <p>    //     value: specifiedFunction,</p>
    <p>    //     enumerable: false,</p>
    <p>    //     configurable: true,</p>
    <p>    //     writable: true</p>
    <p>    // };</p>
    <p>    descriptor.writable = false;</p>
    <p>    return descriptor;</p>
    <p>}</p>
    <p>readonly(Person.prototype, 'name', descriptor);</p>
    <p>// 类似于</p>
    <p>Object.defineProperty(Person.prototype, 'name', descriptor);</p>
    <p>修饰器第一个参数是类的原型对象，上例是Person.prototype，修饰器的本意是要“修饰”类的实例，但是这个时候实例还没生成，所以只能去修饰原型（这不同于类的修饰，那种情况时target参数指的是类本身）；第二个参数是所要修饰的属性名，第三个参数是该属性的描述对象。</p>
    <p>另外，上面代码说明，修饰器（readonly）会修改属性的描述对象（descriptor），然后被修改的描述对象再用来定义属性。</p>
    <p>下面是另一个例子，修改属性描述对象的enumerable属性，使得该属性不可遍历。</p>
    <p>class Person {</p>
    <p>    @nonenumerable</p>
    <p>    get kidCount() { return this.children.length; }</p>
    <p>}</p>
    <p>function nonenumerable(target, name, descriptor) {</p>
    <p>    descriptor.enumerable = false;</p>
    <p>    return descriptor;</p>
    <p>}</p>
    <p>下面的@log修饰器，可以起到输出日志的作用。</p>
    <p>class Math {</p>
    <p>    @log</p>
    <p>    add(a, b) {</p>
    <p>        return a + b;</p>
    <p>    }</p>
    <p>}</p>
    <p>function log(target, name, descriptor) {</p>
    <p>    var oldValue = descriptor.value;</p>
    <p>    descriptor.value = function() {</p>
    <p>        console.log(`Calling ${name} with`, arguments);</p>
    <p>        return oldValue.apply(this, arguments);</p>
    <p>    };</p>
    <p>    return descriptor;</p>
    <p>}</p>
    <p>const math = new Math();</p>
    <p>// passed parameters should get logged now</p>
    <p>math.add(2, 4);</p>
    <p>上面代码中，@log修饰器的作用就是在执行原始的操作之前，执行一次console.log，从而达到输出日志的目的。</p>
    <p>修饰器有注释的作用。</p>
    <p>@testable</p>
    <p>class Person {</p>
    <p>    @readonly</p>
    <p>    @nonenumerable</p>
    <p>    name() { return `${this.first} ${this.last}` }</p>
    <p>}</p>
    <p>从上面代码中，我们一眼就能看出，Person类是可测试的，而name方法是只读和不可枚举的。</p>
    <p>下面是使用 Decorator 写法的组件，看上去一目了然。</p>
    <p>@Component({</p>
    <p>    tag: 'my-component',</p>
    <p>    styleUrl: 'my-component.scss'</p>
    <p>})</p>
    <p>export class MyComponent {</p>
    <p>    @Prop() first: string;</p>
    <p>    @Prop() last: string;</p>
    <p>    @State() isVisible: boolean = true;</p>
    <p>    render() {</p>
    <p>        return (</p>
    <p>            <p>Hello, my name is {this.first} {this.last}</p></p>
    <p>        );</p>
    <p>    }</p>
    <p>}</p>
    <p>如果同一个方法有多个修饰器，会像剥洋葱一样，先从外到内进入，然后由内向外执行。</p>
    <p>function dec(id){</p>
    <p>    console.log('evaluated', id);</p>
    <p>    return (target, property, descriptor) => console.log('executed', id);</p>
    <p>}</p>
    <p>class Example {</p>
    <p>        @dec(1)</p>
    <p>        @dec(2)</p>
    <p>        method(){}</p>
    <p>}</p>
    <p>// evaluated 1</p>
    <p>// evaluated 2</p>
    <p>// executed 2</p>
    <p>// executed 1</p>
    <p>上面代码中，外层修饰器@dec(1)先进入，但是内层修饰器@dec(2)先执行。</p>
    <p>除了注释，修饰器还能用来类型检查。所以，对于类来说，这项功能相当有用。从长期来看，它将是 JavaScript 代码静态分析的重要工具。</p>
    <p>为什么修饰器不能用于函数？</p>
    <p>修饰器只能用于类和类的方法，不能用于函数，因为存在函数提升。</p>
    <p>var counter = 0;</p>
    <p>var add = function () {</p>
    <p>    counter++;</p>
    <p>};</p>
    <p>@add</p>
    <p>function foo() {</p>
    <p>}</p>
    <p>上面的代码，意图是执行后counter等于 1，但是实际上结果是counter等于 0。因为函数提升，使得实际执行的代码是下面这样。</p>
    <p>@add</p>
    <p>function foo() {</p>
    <p>}</p>
    <p>var counter;</p>
    <p>var add;</p>
    <p>counter = 0;</p>
    <p>add = function () {</p>
    <p>    counter++;</p>
    <p>};</p>
    <p>下面是另一个例子。</p>
    <p>var readOnly = require("some-decorator");</p>
    <p>@readOnly</p>
    <p>function foo() {</p>
    <p>}</p>
    <p>上面代码也有问题，因为实际执行是下面这样。</p>
    <p>var readOnly;</p>
    <p>@readOnly</p>
    <p>function foo() {</p>
    <p>}</p>
    <p>readOnly = require("some-decorator");</p>
    <p>总之，由于存在函数提升，使得修饰器不能用于函数。类是不会提升的，所以就没有这方面的问题。</p>
    <p>另一方面，如果一定要修饰函数，可以采用高阶函数的形式直接执行。</p>
    <p>function doSomething(name) {</p>
    <p>    console.log('Hello, ' + name);</p>
    <p>}</p>
    <p>function loggingDecorator(wrapped) {</p>
    <p>    return function() {</p>
    <p>        console.log('Starting');</p>
    <p>        const result = wrapped.apply(this, arguments);</p>
    <p>        console.log('Finished');</p>
    <p>        return result;</p>
    <p>    }</p>
    <p>}</p>
    <p>const wrapped = loggingDecorator(doSomething);</p>
    <p>core-decorators.js</p>
    <p>core-decorators.js是一个第三方模块，提供了几个常见的修饰器，通过它可以更好地理解修饰器。</p>
    <p>（1）@autobind</p>
    <p>autobind修饰器使得方法中的this对象，绑定原始对象。</p>
    <p>import { autobind } from 'core-decorators';</p>
    <p>class Person {</p>
    <p>    @autobind</p>
    <p>    getPerson() {</p>
    <p>        return this;</p>
    <p>    }</p>
    <p>}</p>
    <p>let person = new Person();</p>
    <p>let getPerson = person.getPerson;</p>
    <p>getPerson() === person;</p>
    <p>// true</p>
    <p>（2）@readonly</p>
    <p>readonly修饰器使得属性或方法不可写。</p>
    <p>import { readonly } from 'core-decorators';</p>
    <p>class Meal {</p>
    <p>    @readonly</p>
    <p>    entree = 'steak';</p>
    <p>}</p>
    <p>var dinner = new Meal();</p>
    <p>dinner.entree = 'salmon';</p>
    <p>// Cannot assign to read only property 'entree' of [object Object]</p>
    <p>（3）@override</p>
    <p>override修饰器检查子类的方法，是否正确覆盖了父类的同名方法，如果不正确会报错。</p>
    <p>import { override } from 'core-decorators';</p>
    <p>class Parent {</p>
    <p>    speak(first, second) {}</p>
    <p>}</p>
    <p>class Child extends Parent {</p>
    <p>    @override</p>
    <p>    speak() {}</p>
    <p>    // SyntaxError: Child#speak() does not properly override Parent#speak(first, second)</p>
    <p>}</p>
    <p>// or</p>
    <p>class Child extends Parent {</p>
    <p>    @override</p>
    <p>    speaks() {}</p>
    <p>    // SyntaxError: No descriptor matching Child#speaks() was found on the prototype chain.</p>
    <p>    //</p>
    <p>    //     Did you mean "speak"?</p>
    <p>}</p>
    <p>（4）@deprecate (别名@deprecated)</p>
    <p>deprecate或deprecated修饰器在控制台显示一条警告，表示该方法将废除。</p>
    <p>import { deprecate } from 'core-decorators';</p>
    <p>class Person {</p>
    <p>    @deprecate</p>
    <p>    facepalm() {}</p>
    <p>    @deprecate('We stopped facepalming')</p>
    <p>    facepalmHard() {}</p>
    <p>    @deprecate('We stopped facepalming', { url: 'http://knowyourmeme.com/memes/facepalm' })</p>
    <p>    facepalmHarder() {}</p>
    <p>}</p>
    <p>let person = new Person();</p>
    <p>person.facepalm();</p>
    <p>// DEPRECATION Person#facepalm: This function will be removed in future versions.</p>
    <p>person.facepalmHard();</p>
    <p>// DEPRECATION Person#facepalmHard: We stopped facepalming</p>
    <p>person.facepalmHarder();</p>
    <p>// DEPRECATION Person#facepalmHarder: We stopped facepalming</p>
    <p>//</p>
    <p>//         See http://knowyourmeme.com/memes/facepalm for more details.</p>
    <p>//</p>
    <p>（5）@suppressWarnings</p>
    <p>suppressWarnings修饰器抑制deprecated修饰器导致的console.warn()调用。但是，异步代码发出的调用除外。</p>
    <p>import { suppressWarnings } from 'core-decorators';</p>
    <p>class Person {</p>
    <p>    @deprecated</p>
    <p>    facepalm() {}</p>
    <p>    @suppressWarnings</p>
    <p>    facepalmWithoutWarning() {</p>
    <p>        this.facepalm();</p>
    <p>    }</p>
    <p>}</p>
    <p>let person = new Person();</p>
    <p>person.facepalmWithoutWarning();</p>
    <p>// no warning is logged</p>
    <p>使用修饰器实现自动发布事件</p>
    <p>我们可以使用修饰器，使得对象的方法被调用时，自动发出一个事件。</p>
    <p>const postal = require("postal/lib/postal.lodash");</p>
    <p>export default function publish(topic, channel) {</p>
    <p>    const channelName = channel || '/';</p>
    <p>    const msgChannel = postal.channel(channelName);</p>
    <p>    msgChannel.subscribe(topic, v => {</p>
    <p>        console.log('频道: ', channelName);</p>
    <p>        console.log('事件: ', topic);</p>
    <p>        console.log('数据: ', v);</p>
    <p>    });</p>
    <p>    return function(target, name, descriptor) {</p>
    <p>        const fn = descriptor.value;</p>
    <p>        descriptor.value = function() {</p>
    <p>            let value = fn.apply(this, arguments);</p>
    <p>            msgChannel.publish(topic, value);</p>
    <p>        };</p>
    <p>    };</p>
    <p>}</p>
    <p>上面代码定义了一个名为publish的修饰器，它通过改写descriptor.value，使得原方法被调用时，会自动发出一个事件。它使用的事件“发布/订阅”库是Postal.js。</p>
    <p>它的用法如下。</p>
    <p>// index.js</p>
    <p>import publish from './publish';</p>
    <p>class FooComponent {</p>
    <p>    @publish('foo.some.message', 'component')</p>
    <p>    someMethod() {</p>
    <p>        return { my: 'data' };</p>
    <p>    }</p>
    <p>    @publish('foo.some.other')</p>
    <p>    anotherMethod() {</p>
    <p>        // ...</p>
    <p>    }</p>
    <p>}</p>
    <p>let foo = new FooComponent();</p>
    <p>foo.someMethod();</p>
    <p>foo.anotherMethod();</p>
    <p>以后，只要调用someMethod或者anotherMethod，就会自动发出一个事件。</p>
    <p>$ bash-node index.js</p>
    <p>频道:    component</p>
    <p>事件:    foo.some.message</p>
    <p>数据:    { my: 'data' }</p>
    <p>频道:    /</p>
    <p>事件:    foo.some.other</p>
    <p>数据:    undefined</p>
    <p>Mixin</p>
    <p>在修饰器的基础上，可以实现Mixin模式。所谓Mixin模式，就是对象继承的一种替代方案，中文译为“混入”（mix in），意为在一个对象之中混入另外一个对象的方法。</p>
    <p>请看下面的例子。</p>
    <p>const Foo = {</p>
    <p>    foo() { console.log('foo') }</p>
    <p>};</p>
    <p>class MyClass {}</p>
    <p>Object.assign(MyClass.prototype, Foo);</p>
    <p>let obj = new MyClass();</p>
    <p>obj.foo() // 'foo'</p>
    <p>上面代码之中，对象Foo有一个foo方法，通过Object.assign方法，可以将foo方法“混入”MyClass类，导致MyClass的实例obj对象都具有foo方法。这就是“混入”模式的一个简单实现。</p>
    <p>下面，我们部署一个通用脚本mixins.js，将 Mixin 写成一个修饰器。</p>
    <p>export function mixins(...list) {</p>
    <p>    return function (target) {</p>
    <p>        Object.assign(target.prototype, ...list);</p>
    <p>    };</p>
    <p>}</p>
    <p>然后，就可以使用上面这个修饰器，为类“混入”各种方法。</p>
    <p>import { mixins } from './mixins';</p>
    <p>const Foo = {</p>
    <p>    foo() { console.log('foo') }</p>
    <p>};</p>
    <p>@mixins(Foo)</p>
    <p>class MyClass {}</p>
    <p>let obj = new MyClass();</p>
    <p>obj.foo() // "foo"</p>
    <p>通过mixins这个修饰器，实现了在MyClass类上面“混入”Foo对象的foo方法。</p>
    <p>不过，上面的方法会改写MyClass类的prototype对象，如果不喜欢这一点，也可以通过类的继承实现 Mixin。</p>
    <p>class MyClass extends MyBaseClass {</p>
    <p>    /* ... */</p>
    <p>}</p>
    <p>上面代码中，MyClass继承了MyBaseClass。如果我们想在MyClass里面“混入”一个foo方法，一个办法是在MyClass和MyBaseClass之间插入一个混入类，这个类具有foo方法，并且继承了MyBaseClass的所有方法，然后MyClass再继承这个类。</p>
    <p>let MyMixin = (superclass) => class extends superclass {</p>
    <p>    foo() {</p>
    <p>        console.log('foo from MyMixin');</p>
    <p>    }</p>
    <p>};</p>
    <p>上面代码中，MyMixin是一个混入类生成器，接受superclass作为参数，然后返回一个继承superclass的子类，该子类包含一个foo方法。</p>
    <p>接着，目标类再去继承这个混入类，就达到了“混入”foo方法的目的。</p>
    <p>class MyClass extends MyMixin(MyBaseClass) {</p>
    <p>    /* ... */</p>
    <p>}</p>
    <p>let c = new MyClass();</p>
    <p>c.foo(); // "foo from MyMixin"</p>
    <p>如果需要“混入”多个方法，就生成多个混入类。</p>
    <p>class MyClass extends Mixin1(Mixin2(MyBaseClass)) {</p>
    <p>    /* ... */</p>
    <p>}</p>
    <p>这种写法的一个好处，是可以调用super，因此可以避免在“混入”过程中覆盖父类的同名方法。</p>
    <p>let Mixin1 = (superclass) => class extends superclass {</p>
    <p>    foo() {</p>
    <p>        console.log('foo from Mixin1');</p>
    <p>        if (super.foo) super.foo();</p>
    <p>    }</p>
    <p>};</p>
    <p>let Mixin2 = (superclass) => class extends superclass {</p>
    <p>    foo() {</p>
    <p>        console.log('foo from Mixin2');</p>
    <p>        if (super.foo) super.foo();</p>
    <p>    }</p>
    <p>};</p>
    <p>class S {</p>
    <p>    foo() {</p>
    <p>        console.log('foo from S');</p>
    <p>    }</p>
    <p>}</p>
    <p>class C extends Mixin1(Mixin2(S)) {</p>
    <p>    foo() {</p>
    <p>        console.log('foo from C');</p>
    <p>        super.foo();</p>
    <p>    }</p>
    <p>}</p>
    <p>上面代码中，每一次混入发生时，都调用了父类的super.foo方法，导致父类的同名方法没有被覆盖，行为被保留了下来。</p>
    <p>new C().foo()</p>
    <p>// foo from C</p>
    <p>// foo from Mixin1</p>
    <p>// foo from Mixin2</p>
    <p>// foo from S</p>
    <p>Trait</p>
    <p>Trait 也是一种修饰器，效果与 Mixin 类似，但是提供更多功能，比如防止同名方法的冲突、排除混入某些方法、为混入的方法起别名等等。</p>
    <p>下面采用traits-decorator这个第三方模块作为例子。这个模块提供的traits修饰器，不仅可以接受对象，还可以接受 ES6 类作为参数。</p>
    <p>import { traits } from 'traits-decorator';</p>
    <p>class TFoo {</p>
    <p>    foo() { console.log('foo') }</p>
    <p>}</p>
    <p>const TBar = {</p>
    <p>    bar() { console.log('bar') }</p>
    <p>};</p>
    <p>@traits(TFoo, TBar)</p>
    <p>class MyClass { }</p>
    <p>let obj = new MyClass();</p>
    <p>obj.foo() // foo</p>
    <p>obj.bar() // bar</p>
    <p>上面代码中，通过traits修饰器，在MyClass类上面“混入”了TFoo类的foo方法和TBar对象的bar方法。</p>
    <p>Trait 不允许“混入”同名方法。</p>
    <p>import { traits } from 'traits-decorator';</p>
    <p>class TFoo {</p>
    <p>    foo() { console.log('foo') }</p>
    <p>}</p>
    <p>const TBar = {</p>
    <p>    bar() { console.log('bar') },</p>
    <p>    foo() { console.log('foo') }</p>
    <p>};</p>
    <p>@traits(TFoo, TBar)</p>
    <p>class MyClass { }</p>
    <p>// 报错</p>
    <p>// throw new Error('Method named: ' + methodName + ' is defined twice.');</p>
    <p>//                ^</p>
    <p>// Error: Method named: foo is defined twice.</p>
    <p>上面代码中，TFoo和TBar都有foo方法，结果traits修饰器报错。</p>
    <p>一种解决方法是排除TBar的foo方法。</p>
    <p>import { traits, excludes } from 'traits-decorator';</p>
    <p>class TFoo {</p>
    <p>    foo() { console.log('foo') }</p>
    <p>}</p>
    <p>const TBar = {</p>
    <p>    bar() { console.log('bar') },</p>
    <p>    foo() { console.log('foo') }</p>
    <p>};</p>
    <p>@traits(TFoo, TBar::excludes('foo'))</p>
    <p>class MyClass { }</p>
    <p>let obj = new MyClass();</p>
    <p>obj.foo() // foo</p>
    <p>obj.bar() // bar</p>
    <p>上面代码使用绑定运算符（::）在TBar上排除foo方法，混入时就不会报错了。</p>
    <p>另一种方法是为TBar的foo方法起一个别名。</p>
    <p>import { traits, alias } from 'traits-decorator';</p>
    <p>class TFoo {</p>
    <p>    foo() { console.log('foo') }</p>
    <p>}</p>
    <p>const TBar = {</p>
    <p>    bar() { console.log('bar') },</p>
    <p>    foo() { console.log('foo') }</p>
    <p>};</p>
    <p>@traits(TFoo, TBar::alias({foo: 'aliasFoo'}))</p>
    <p>class MyClass { }</p>
    <p>let obj = new MyClass();</p>
    <p>obj.foo() // foo</p>
    <p>obj.aliasFoo() // foo</p>
    <p>obj.bar() // bar</p>
    <p>上面代码为TBar的foo方法起了别名aliasFoo，于是MyClass也可以混入TBar的foo方法了。</p>
    <p>alias和excludes方法，可以结合起来使用。</p>
    <p>@traits(TExample::excludes('foo','bar')::alias({baz:'exampleBaz'}))</p>
    <p>class MyClass {}</p>
    <p>上面代码排除了TExample的foo方法和bar方法，为baz方法起了别名exampleBaz。</p>
    <p>as方法则为上面的代码提供了另一种写法。</p>
    <p>@traits(TExample::as({excludes:['foo', 'bar'], alias: {baz: 'exampleBaz'}}))</p>
    <p>class MyClass {}</p>
    <p>Babel 转码器的支持</p>
    <p>目前，Babel 转码器已经支持 Decorator。</p>
    <p>首先，安装@babel/core和@babel/plugin-transform-decorators。由于后者包括在@babel/preset-stage-0之中，所以改为安装@babel/preset-stage-0亦可。</p>
    <p>$ npm install @babel/core @babel/plugin-transform-decorators</p>
    <p>然后，设置配置文件.babelrc。</p>
    <p>{</p>
    <p>    "plugins": ["transform-decorators"]</p>
    <p>}</p>
    <p>这时，Babel 就可以对 Decorator 转码了。</p>
    <p>脚本中打开的命令如下。</p>
    <p>babel.transform("code", {plugins: ["transform-decorators"]})</p>
    <p>Babel 的官方网站提供一个在线转码器，只要勾选 Experimental，就能支持 Decorator 的在线转码。</p>
</div>
